

module IIC_IF (
    input					RST						,	// async. reset
	input					CLK						,	// clock 50MHz

	input		[6:0]		SLV_ADR					,	// Slave Address
	input					SER_SEL					,	// Serial line select (0:Disable 1:Enable)
	input					SCL						,	// IIC Serial Clock
	input					SDA_I					,	// IIC Serial Input
	output					SDA_O					,	// IIC Serial Output
	output					SDA_OE					,	// IIC Serial Output enable (1:HiZ 0:Output)

	output					REG_RD					,	// REG I/F read enable
	output					REG_WR					,	// REG I/F write enable
	output		[15:0]		REG_ADDR				,	// REG I/F address
	output		[31:0]		REG_WDAT				,	// REG I/F write data
	input		[31:0]		REG_RDAT					// REG I/F read data
    
) ;


	//-----------------------------------------------
	// Declare the parameter
	//-----------------------------------------------
	parameter				P_FIL_LEN  = 4'D11		;	// 210ns @ 50MHz
	parameter				P_CDLY_LEN = 4'D3		;	//  60ns @ 50MHz
	parameter				P_DDLY_LEN = 4'D3		;	//  60ns @ 50MHz


	// ---------------------------------------------------------------------
	// Defination of Internal Signals
	// ---------------------------------------------------------------------
	// input buffer
	reg						r_SCL_SYNC				;	// SCL Input Buffer Sync
	reg						r_SDA_SYNC				;	// SDA Input Buffer Sync
	reg						r_SCL					;	// SCL Input Buffer
	reg						r_SDA					;	// SDA Input Buffer
	// input filter
	reg		[P_FIL_LEN-1:0]	r_SCL_FIL_SHT			;	// SCL Filter Shift
	reg		[P_FIL_LEN-1:0]	r_SDA_FIL_SHT			;	// SDA Filter Shift
	reg						r_SCL_FIL				;	// SCL Filter
	reg						r_SDA_FIL				;	// SDA Filter
	// SCL / SDA delay
	reg		[P_CDLY_LEN-1:0]r_SCL_DLY_SHT			;	// SCL Delay Shift
	reg		[P_DDLY_LEN-1:0]r_SDA_DLY_SHT			;	// SCL Delay Shift
	// IIC Start & Stop
	wire					s_SCL_RISING			;	// SCL Rising Edge
	wire					s_SCL_FALLING			;	// SCL Falling Edge
	wire					s_SDA_RISING			;	// SDA Rising Edge
	wire					s_SDA_FALLING			;	// SDA Falling Edge
	wire					s_IIC_START				;	// IIC Command Start
	wire					s_IIC_STOP				;	// IIC Command Stop
	// IIC FSM
	parameter				P_IDLE		= 3'D0		;	// IDLE
	parameter				P_START		= 3'D1		;	// IIC START
	parameter				P_ADDRH		= 3'D2		;	// IIC Address[15:8]
	parameter				P_ADDRL		= 3'D3		;	// IIC Address[ 7:0]
	parameter				P_READ		= 3'D4		;	// IIC Read
	parameter				P_WRITE		= 3'D5		;	// IIC Write
	reg			[ 2:0]		r_STATE					;	// IIC State Control
	// IIC CNT
	reg			[ 3:0]		r_IIC_COUNT				;	// IIC Receive Count
	wire					s_IIC_ACKTIM			;	// IIC ACK Timming
	wire					s_IIC_TXD_LD			;	// IIC TX Data Load
	//	IIC RX DATA
	reg			[ 7:0]		r_RXD_SHT				;	// IIC RX Data Shift
	wire					s_IIC_ADR_SEL			;	// IIC Address select
	//	IIC TX DATA
	reg			[ 7:0]		r_IIC_TXD_SHT			;	// IIC TX Data Shift
	wire					s_IIC_AH_EN				;	// IIC Address[15:8] Load
	wire					s_IIC_AL_EN				;	// IIC Address[ 7:0] Load
	wire					s_IIC_WR_EN				;	// IIC Write Enable
	wire					s_IIC_FRD_EN			;	// IIC First Read Enable
	wire					s_IIC_CRD_EN			;	// IIC Continue Read Enable
	reg			[15:0]		r_IIC_ADDR				;	// IIC Address
	//	REG I/F
	reg						r_REG_RD				;	// REG Read
	reg						r_REG_WR				;	// REG Write
	reg			[31:0]		r_REG_WDAT				;	// REG Write data
	reg			[15:0]		r_REG_ADDR				;	// REG Addresss

// =========================================================================
// RTL Body Begin
// =========================================================================

	//----------------------------------------------------------------
	//	Output
	//----------------------------------------------------------------
	assign SDA_O		= r_IIC_TXD_SHT[7]			;
	assign SDA_OE		= r_IIC_TXD_SHT[7]			;

	assign REG_RD		= r_REG_RD					;
	assign REG_WR		= r_REG_WR					;
	assign REG_ADDR		= {r_REG_ADDR[15:2], 2'B00}	;
	assign REG_WDAT		= r_REG_WDAT				;


	//----------------------------------------------------------------
	//	input buffer (SYNC.)
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SCL_SYNC				<= 1'B1			;
			r_SDA_SYNC				<= 1'B1			;
		end else begin
			r_SCL_SYNC				<= SCL			;	// sync FF #1
			r_SDA_SYNC				<= SDA_I		;
		end
	end

	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SCL					<= 1'B1			;
			r_SDA					<= 1'B1			;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_SCL				<= 1'B1			;
				r_SDA				<= 1'B1			;
			end else begin
				r_SCL				<= r_SCL_SYNC	;	// sync FF #2
				r_SDA				<= r_SDA_SYNC	;
			end
		end
	end


	//----------------------------------------------------------------
	//	input filter
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SCL_FIL_SHT			<= { P_FIL_LEN{1'B1} } ;
			r_SDA_FIL_SHT			<= { P_FIL_LEN{1'B1} } ;
		end else begin
			r_SCL_FIL_SHT			<= {r_SCL_FIL_SHT[(P_FIL_LEN-2):0],  r_SCL}	;
			r_SDA_FIL_SHT			<= {r_SDA_FIL_SHT[(P_FIL_LEN-2):0],  r_SDA}	;
		end
	end

	// r_SCL_FIL
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SCL_FIL				<= 1'B1	;
		end else begin
			if ( r_SCL_FIL_SHT == {P_FIL_LEN{1'B1}} ) begin
				r_SCL_FIL			<= 1'B1	;
			end else if ( r_SCL_FIL_SHT == {P_FIL_LEN{1'B0}} ) begin
				r_SCL_FIL			<= 1'B0	;
			end
		end
	end

	// r_SDA_FIL
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SDA_FIL				<= 1'B1	;
		end else begin
			if ( r_SDA_FIL_SHT == {P_FIL_LEN{1'B1}} ) begin
				r_SDA_FIL			<= 1'B1	;
			end else if ( r_SDA_FIL_SHT == {P_FIL_LEN{1'B0}} ) begin
				r_SDA_FIL			<= 1'B0	;
			end
		end
	end


	//----------------------------------------------------------------
	//	SCL / SDA delay
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_SCL_DLY_SHT			<= { P_CDLY_LEN{1'B1} };
			r_SDA_DLY_SHT			<= { P_DDLY_LEN{1'B1} };
		end else begin
			r_SCL_DLY_SHT			<= { r_SCL_DLY_SHT[(P_CDLY_LEN-2):0],  r_SCL_FIL } ;
			r_SDA_DLY_SHT			<= { r_SDA_DLY_SHT[(P_DDLY_LEN-2):0],  r_SDA_FIL } ;
		end
	end


	//----------------------------------------------------------------
	//	IIC Start & Stop
	//----------------------------------------------------------------
	assign s_SCL_RISING		= (r_SCL_DLY_SHT[(P_CDLY_LEN-1) : (P_CDLY_LEN-2)] == 2'B01) ? 1'B1 : 1'B0	;
	assign s_SCL_FALLING	= (r_SCL_DLY_SHT[(P_CDLY_LEN-1) : (P_CDLY_LEN-2)] == 2'B10) ? 1'B1 : 1'B0	;
	assign s_SDA_RISING		= (r_SDA_DLY_SHT[(P_DDLY_LEN-1) : (P_DDLY_LEN-2)] == 2'B01) ? 1'B1 : 1'B0	;
	assign s_SDA_FALLING	= (r_SDA_DLY_SHT[(P_DDLY_LEN-1) : (P_DDLY_LEN-2)] == 2'B10) ? 1'B1 : 1'B0	;

	assign s_IIC_START		= r_SCL_FIL & s_SDA_FALLING	;
	assign s_IIC_STOP		= r_SCL_FIL & s_SDA_RISING	;


	//----------------------------------------------------------------
	//	IIC FSM
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_STATE					<= P_IDLE	;
		end else begin

		case ( r_STATE )
			P_IDLE : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_START == 1'B1 ) begin
					r_STATE			<= P_START	;
				end else begin
					r_STATE			<= P_IDLE	;
				end
			end

			P_START : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_STOP == 1'B1 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( (s_SCL_RISING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) begin
					if ( (s_IIC_ADR_SEL == 1'B1) && (r_RXD_SHT[0] == 1'B0) ) begin
						r_STATE		<= P_ADDRH	;				// when Write Command
					end else if ( (s_IIC_ADR_SEL == 1'B1) && (r_RXD_SHT[0] == 1'B1) ) begin
						r_STATE		<= P_READ	;				// when Read Command
					end else begin
						r_STATE		<= P_IDLE	;				// return to IDLE
					end
				end
			end

			P_ADDRH : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_STOP == 1'B1 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_START == 1'B1 ) begin
					r_STATE			<= P_START	;
				end else if ( (s_SCL_RISING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) begin
					r_STATE			<= P_ADDRL	;
				end
			end

			P_ADDRL : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_STOP == 1'B1 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_START == 1'B1 ) begin
					r_STATE			<= P_START	;
				end else if ( (s_SCL_RISING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) begin
					r_STATE			<= P_WRITE	;
				end
			end

			P_READ : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_STOP == 1'B1 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_START == 1'B1 ) begin
					r_STATE			<= P_START	;
				end
			end

			P_WRITE : begin
				if ( SER_SEL == 1'B0 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_STOP == 1'B1 ) begin
					r_STATE			<= P_IDLE	;
				end else if ( s_IIC_START == 1'B1 ) begin
					r_STATE			<= P_START	;
				end
			end

			default : begin
				r_STATE				<= P_IDLE	;
			end

		endcase
		end
	end


	//----------------------------------------------------------------
	//	IIC CNT
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_IIC_COUNT				<= 4'D0					;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_IIC_COUNT			<= 4'D0					;
			end else if ( s_IIC_START == 1'B1 ) begin
				r_IIC_COUNT			<= 4'D0					;
			end else if ( s_SCL_RISING == 1'B1 ) begin
				if ( r_STATE == P_IDLE ) begin
					r_IIC_COUNT		<= 4'D0					;	// clear when IDLE
				end else if ( r_IIC_COUNT == 4'D8 ) begin
					r_IIC_COUNT		<= 4'D0					;	// clear when reach 8
				end else begin
					r_IIC_COUNT		<= r_IIC_COUNT + 4'D1	;
				end
			end
		end
	end

	assign s_IIC_ACKTIM		= (r_IIC_COUNT == 4'D8) ? 1'B1 : 1'B0	;
	assign s_IIC_TXD_LD		= (r_IIC_COUNT == 4'D0) ? 1'B1 : 1'B0	;


	//----------------------------------------------------------------
	//	IIC RX DATA
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_RXD_SHT				<= 8'D0 ;
		end else begin
			if ( r_STATE == P_IDLE ) begin
				r_RXD_SHT			<= 8'D0 ;
			end else if	( s_SCL_RISING == 1'B1 ) begin
				r_RXD_SHT			<= { r_RXD_SHT[6:0], r_SDA_FIL } ;
			end
		end
	end


	assign s_IIC_ADR_SEL	= ( r_RXD_SHT[7:1] == SLV_ADR ) ? 1'B1 : 1'B0	;


	//----------------------------------------------------------------
	//	IIC TX DATA
	//----------------------------------------------------------------
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_IIC_TXD_SHT			<= 8'HFF ;
		end else begin
			if ( r_STATE == P_IDLE ) begin
				r_IIC_TXD_SHT		<= 8'HFF ;
			end else if	( s_SCL_FALLING == 1'B1 ) begin
				if ( (r_STATE == P_START) && (s_IIC_ACKTIM == 1'B1) && (s_IIC_ADR_SEL == 1'B1) ) begin
					r_IIC_TXD_SHT	<= 8'H7F ;
				end else if ( (r_STATE == P_ADDRH) && (s_IIC_ACKTIM == 1'B1) ) begin
				 	r_IIC_TXD_SHT	<= 8'H7F ;
				end else if ( (r_STATE == P_ADDRL) && (s_IIC_ACKTIM == 1'B1) ) begin
				 	r_IIC_TXD_SHT	<= 8'H7F ;
				end else if ( (r_STATE == P_WRITE) && (s_IIC_ACKTIM == 1'B1) ) begin
				 	r_IIC_TXD_SHT	<= 8'H7F ;
				end else if ( (r_STATE == P_READ) && (s_IIC_TXD_LD == 1'B1) && (r_RXD_SHT[0] == 1'B0) ) begin
					r_IIC_TXD_SHT	<= (r_REG_ADDR[1:0] == 2'B00) 	  ? REG_RDAT[31:24] :
									   (r_REG_ADDR[1:0] == 2'B01) 	  ? REG_RDAT[23:16] :
									   (r_REG_ADDR[1:0] == 2'B10) 	  ? REG_RDAT[15: 8] :
									           	                 		REG_RDAT[ 7: 0] ;
				end else begin
					r_IIC_TXD_SHT	<= {r_IIC_TXD_SHT[6:0], 1'B1} ;
				end
			end
		end
	end


	assign s_IIC_AH_EN	= ( (r_STATE == P_ADDRH) && (s_SCL_FALLING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) ? 1'B1 : 1'B0 ;

	assign s_IIC_AL_EN	= ( (r_STATE == P_ADDRL) && (s_SCL_FALLING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) ? 1'B1 : 1'B0 ;

	assign s_IIC_WR_EN	= ( (r_STATE == P_WRITE) && (s_SCL_FALLING == 1'B1) && (s_IIC_ACKTIM == 1'B1) ) ? 1'B1 : 1'B0 ;

	assign s_IIC_FRD_EN	= ( (r_STATE == P_START) && (s_SCL_FALLING == 1'B1) && (s_IIC_ACKTIM == 1'B1) &&
													(s_IIC_ADR_SEL == 1'B1) && (r_RXD_SHT[0] == 1'B1) ) ? 1'B1 : 1'B0 ;

	assign s_IIC_CRD_EN	= ( (r_STATE == P_READ)  && (s_SCL_RISING  == 1'B1) && (s_IIC_ACKTIM == 1'B1) && (r_SDA_FIL == 1'B0) ) ? 1'B1 : 1'B0 ;


	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_IIC_ADDR				<= 16'D0				;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_IIC_ADDR			<= 16'D0				;
			end else if ( s_IIC_AH_EN == 1'B1 ) begin
				r_IIC_ADDR[15:8]	<= r_RXD_SHT			;	// ADDR[15:8] Load
			end else if ( s_IIC_AL_EN == 1'B1 ) begin
				r_IIC_ADDR[ 7:0]	<= r_RXD_SHT			;	// ADDR[ 7:0] Load
			end else if ( s_IIC_WR_EN == 1'B1 ) begin
				r_IIC_ADDR			<= r_IIC_ADDR + 16'D1	;	// 8 Bit Write
			end else if ( s_IIC_FRD_EN == 1'B1 ) begin
				r_IIC_ADDR			<= r_IIC_ADDR + 16'D1	;	// 8 Bit First Read
			end else if ( s_IIC_CRD_EN == 1'B1 ) begin
				r_IIC_ADDR			<= r_IIC_ADDR + 16'D1	;	// 8 Bit Continue Read
			end
		end
	end

	//----------------------------------------------------------------
	//	REG I/F
	//----------------------------------------------------------------

	// r_REG_RD
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_REG_RD				<= 1'B0		;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_REG_RD			<= 1'B0		;
			end else if ( (s_IIC_FRD_EN == 1'B1) && (r_IIC_ADDR[1:0] == 2'B00) ) begin
				r_REG_RD			<= 1'B1		;		// when IIC First Read (Byte 0 only)
			end else if ( (s_IIC_CRD_EN == 1'B1) && (r_IIC_ADDR[1:0] == 2'B00) ) begin
				r_REG_RD			<= 1'B1		;		// when IIC Continue Read (Byte 0 only)
			end else begin
				r_REG_RD			<= 1'B0		;
			end
		end
	end

	// r_REG_WR
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_REG_WR				<= 1'B0		;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_REG_WR			<= 1'B0		;
			end else if ( (s_IIC_WR_EN == 1'B1) && (r_IIC_ADDR[1:0] == 2'B11) ) begin
				r_REG_WR			<= 1'B1		;		// when IIC Write (Byte 3 only)
			end else begin
				r_REG_WR			<= 1'B0		;
			end
		end
	end

	// r_REG_WDAT
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_REG_WDAT				<= 32'D0	;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_REG_WDAT			<= 32'D0	;
			end else if ( s_IIC_WR_EN == 1'B1 ) begin
				r_REG_WDAT[31:24]	<= (r_IIC_ADDR[1:0] == 2'B00)? r_RXD_SHT : r_REG_WDAT[31:24]	;	// Byte 0
				r_REG_WDAT[23:16]	<= (r_IIC_ADDR[1:0] == 2'B01)? r_RXD_SHT : r_REG_WDAT[23:16]	;	// Byte 1
				r_REG_WDAT[15: 8]	<= (r_IIC_ADDR[1:0] == 2'B10)? r_RXD_SHT : r_REG_WDAT[15: 8]	;	// Byte 2
				r_REG_WDAT[ 7: 0]	<= (r_IIC_ADDR[1:0] == 2'B11)? r_RXD_SHT : r_REG_WDAT[ 7: 0]	;	// Byte 3
			end
		end
	end

	// r_REG_ADDR
	always @ (posedge CLK or posedge RST) begin
		if ( RST == 1'B1 ) begin
			r_REG_ADDR				<= 16'D0			;
		end else begin
			if ( SER_SEL == 1'B0 ) begin
				r_REG_ADDR			<= 16'D0			;
			end else if ( s_IIC_FRD_EN == 1'B1 ) begin
				r_REG_ADDR			<= r_IIC_ADDR		;		// when IIC First Read
			end else if ( s_IIC_CRD_EN == 1'B1 ) begin
				r_REG_ADDR			<= r_IIC_ADDR		;		// when IIC Continue Read
			end else if ( (s_IIC_WR_EN == 1'B1) && (r_IIC_ADDR[1:0] == 2'B11) ) begin
				r_REG_ADDR			<= r_IIC_ADDR		;		// when IIC Write (Byte 3 only)
			end
		end
	end


endmodule